/*
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */
package ru.papergames.battleserver.network;

import org.apache.log4j.Logger;
import org.mmocore.network.*;
import ru.common.util.Util;
import ru.papergames.Config;
import ru.papergames.battleserver.BattleThreadPoolManager;
import ru.papergames.battleserver.network.clientpackets.flashuser.*;

import java.nio.ByteBuffer;
import java.util.concurrent.RejectedExecutionException;

/**
 * Stateful Packet Handler<BR>
 * The Stateful approach prevents the server from handling inconsistent packets, examples:<BR>
 * <li>Clients sends a MoveToLocation packet without having a character attached. (Potential errors handling the packet).</li>
 * <li>Clients sends a RequestAuthLogin being already authed. (Potential exploit).</li>
 * <BR><BR>
 * Note: If for a given exception a packet needs to be handled on more then one state, then it should be added to all these states.
 *
 * @author KenM
 */
public final class PaperGamePacketHandler
        implements
        IPacketHandler<PaperGameClient>,
        IClientFactory<PaperGameClient>,
        IMMOExecutor<PaperGameClient>,
        PaperGameClientPacketTypes {
    private static final Logger _log = Logger.getLogger(PaperGamePacketHandler.class.getName());

    // implementation

    public ReceivablePacket<PaperGameClient> handlePacket(ByteBuffer buf, PaperGameClient client) {
        if (client.isDetached())
            return null;

        int packetId = buf.get() & 0xFF;
        int id3;

        ReceivablePacket<PaperGameClient> msg = null;
        PaperGameClient.GameClientState state = client.getState();

        switch (state) {
            case CONNECTED:
                switch (packetId) {
                    case AUTH_LOGIN_PACKET_ID: // AuthLogin
                        msg = new AuthLogin();
                        break;
                    default:
                        printDebug(packetId, buf, state, client);
                        break;
                }
                break;
            case AUTHED:
                switch (packetId) {
                    case PING_REQUEST_PACKET_ID:
                        msg = new PingRequest();
                        break;
                        //msg = new Logout();
//                        break;
//                    case 0x0c:
//                        msg = new CharacterCreate();
//                        break;
                    default:
                        printDebug(packetId, buf, state, client);
                        break;
                }
                break;
            case IN_BATTLE:
                switch (packetId) {
                    case PING_REQUEST_PACKET_ID:
                        msg = new PingRequest();
                        break;
                    case CLIENT_LOADED_BATTLE_PACKET_ID:
                        msg = new ClientLoadedBattle();
                        break;
                    case BATTLE_MOVE_BALL_PACKET_ID:
                        msg = new BattleMoveBall();
                        break;
                    case BATTLE_TURN_FINISH_PACKET_ID:
                        msg = new BattleTurnFinish();
                        break;
                    default:
                        this.printDebug(packetId, buf, state, client);
                        break;
                }
                break;
        }
        return msg;
    }

    private void printDebug(int opcode, ByteBuffer buf, PaperGameClient.GameClientState state, PaperGameClient client) {
        if (!Config.PACKET_HANDLER_DEBUG)
            return;

        int size = buf.remaining();
        _log.warn("Unknown Packet: " + Integer.toHexString(opcode) + " on State: " + state.name() + " Client: " + client.toString());
        byte[] array = new byte[size];
        buf.get(array);
        _log.warn(Util.printData(array, size));
    }

    private void printDebugDoubleOpcode(int opcode, int id2, ByteBuffer buf, PaperGameClient.GameClientState state, PaperGameClient client) {
        if (!Config.PACKET_HANDLER_DEBUG)
            return;
        int size = buf.remaining();
        _log.warn("Unknown Packet: " + Integer.toHexString(opcode) + ":" + Integer.toHexString(id2) + " on State: " + state.name() + " Client: " + client.toString());
        byte[] array = new byte[size];
        buf.get(array);
        _log.warn(Util.printData(array, size));
    }

    // impl

    public PaperGameClient create(MMOConnection<PaperGameClient> con) {
        return new PaperGameClient(con);
    }

    public void execute(ReceivablePacket<PaperGameClient> rp) {
        try {
            BattleThreadPoolManager.getInstance().executePacket(rp);
        } catch (RejectedExecutionException e) {
            // if the server is shutdown we ignore
            if (!BattleThreadPoolManager.getInstance().isShutdown()) {
                _log.fatal("Failed executing: " + rp.getClass().getSimpleName() + " for Client: " + rp.getClient().toString());
            }
        }
    }
}
